package controllers;

import models.YouUser;
import models.YouUser.FacebookData;
import callbacks.FacebookCallbacks.*;

import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.node.ArrayNode;

import play.libs.F.Function;
import play.libs.F.Option;
import play.libs.F.Promise;
import play.libs.F;
import play.libs.Json;
import play.libs.WS;
import play.libs.WS.Response;
import play.mvc.Controller;
import play.mvc.Result;
import services.FacebookQueryService;
import util.Config;

public class Application extends Controller {

	public static Result index() {
		return redirect(Config.get().fblogin);
	}

	public static Result createUser() {
		YouUser user = YouUser.create();
		return ok(Json.toJson(user));
	}

	// TODO check if token is expired
	public static Result query(String query, Long id, int max) {
		YouUser user = YouUser.find.byId(id);

		if (user == null) {
			return notFound("Unknown id");
		} else {
			Promise<ArrayNode> users = FacebookQueryService.searchUserJSON(
					query, user.fbdata.accessToken);
			return async(users.map(new Function<ArrayNode, Result>() {
				@Override
				public Result apply(ArrayNode json) throws Throwable {
					return ok(json);
				}
			}));
		}
	}

	public static Result me(Long id) {
		YouUser user = YouUser.find.byId(id);
		if (user == null) {
			return notFound("Unknown id");
		} else {
			Promise<JsonNode> users = FacebookQueryService
					.getUserData(user.fbdata.accessToken);
			return async(users.map(new Function<JsonNode, Result>() {
				@Override
				public Result apply(JsonNode arg0) throws Throwable {
					return ok(arg0);
				}
			}));
		}
	}

	public static Result link(Long id, Option<String> callback) {
		final YouUser user = YouUser.find.byId(id);
		if (user == null) {
			return notFound("Unknown id");
		} else {
			try {
				return redirect(Config.get().fblogin
						.replace("$STATE$", id + "$$" + callback.getOrElse("")));	
			} catch (RuntimeException e) {
				return internalServerError(e.getMessage());
			}
		}
	}
	
	public static Result linkCallback(Option<String> code, Option<String> state, Option<String> error) {
		//FIXME I have no fucking idea why play is swapping the contents of error and state.
		if (error.isDefined() && error.get().contains("$$")) {
			error = F.None();
			state = F.Some(error.get());
		} 
		
		if (!code.isDefined()) {
			return badRequest("facebook code is missing");
		} else if (!state.isDefined()) {
			return badRequest("state parameter missing");
		} else {
			if (error.isDefined() && error.get().equals("access_denied")) {
				return unauthorized("You must grant the required permissions!");
			} else {
				String[] stateInfo = state.get().split("\\$\\$");
				long id = Long.parseLong(stateInfo[0]);
				final String redirectUrl;
				if (stateInfo.length > 1) {
					redirectUrl = stateInfo[1]; 
				} else {
					redirectUrl = "";
				}
				final YouUser user = YouUser.find.byId(id);		
				Config config = Config.get();
				Promise<WS.Response> token = getOAuthToken(code, config);
				return async(token.flatMap(new GetInfo()).map(
						new Function<FacebookData, Result>() {
							@Override
							public Result apply(FacebookData data)
									throws Throwable {
								user.updateFacebookData(data);
								if (redirectUrl.equals("")) {
									return ok(Json.toJson(user));
								} else {
									return redirect(redirectUrl + "?id=" + user.youId + "&success=true");
								}
							}
						}));
			}
		}
	}

	public static Promise<Response> getOAuthToken(Option<String> code,
			Config config) {
		return WS.url(config.fboauth)
				.setQueryParameter("client_id", config.fbkey)
				.setQueryParameter("client_secret", config.fbsecret)
				.setQueryParameter("redirect_uri", config.fbredirect)
				.setQueryParameter("code", code.get()).get();
	}

	public static Result unlink(Long id) {
		YouUser user = YouUser.find.byId(id);
		if (user == null) {
			return notFound("Unknown id");
		} else {
			user.clearLinkData();
			return ok("Link data cleared! Remember to remove the permissions you granted"
				+ " to this app on facebook! You will need to login again to use this app.");
		}
	}
}
